package me.spring.cloud.common.components.oss.cloud.qiniucloud;

import com.qiniu.common.Zone;
import com.qiniu.http.Response;
import com.qiniu.storage.BucketManager;
import com.qiniu.storage.BucketManager.BatchOperations;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.BatchStatus;
import com.qiniu.storage.model.DefaultPutRet;
import com.qiniu.storage.model.FetchRet;
import com.qiniu.storage.model.FileInfo;
import com.qiniu.util.Auth;
import me.spring.cloud.common.components.oss.cloud.AbstractCloudStoreServiceImpl;
import me.spring.cloud.common.components.oss.cloud.exception.FileUploadException;
import me.spring.cloud.common.components.oss.cloud.exception.code.OssCode;
import me.spring.cloud.common.components.util.constant.CharConstant;
import me.spring.cloud.common.components.util.io.FileHelper;
import me.spring.cloud.common.components.util.lang.StringUtil;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import javax.validation.constraints.NotNull;

/**
 * 七牛云存储
 *
 * @author huangzhibin
 * @date 2018-07-25 15:41
 */
public class QiniuCloudStorageServiceImpl extends AbstractCloudStoreServiceImpl {

  private UploadManager uploadManager;
  private String token;

  private BucketManager bucketManager;

  public QiniuCloudStorageServiceImpl(QiniuCloudConfig config) {
    super.setConfig(config);
    Configuration cfg = new Configuration(Zone.autoZone());
    this.uploadManager = new UploadManager(cfg);
    Auth auth = Auth.create(config.getQiniuAccessKey(), config.getQiniuSecretKey());
    token = auth.uploadToken(config.getBucketName());
    this.bucketManager = new BucketManager(auth, cfg);
  }

  /**
   * @param res
   * @Return java.lang.String
   * @Author luffy
   * @Description 拼接七牛云文件访问url
   * @Date 2019-07-05 15:44
   */
  private String getUrl(@NotNull Response res) throws Exception {
    if (!res.isOK()) {
      throw new FileUploadException(OssCode.FILE_UPLOAD_FAILED, "七牛上传文件出错：" + res.error);
    }
    if (this.getConfig().getDomain().endsWith(String.valueOf(CharConstant.SLASH))) {
      return StringUtil.append(this.getConfig().getDomain(), res.jsonToObject(DefaultPutRet.class).key);
    }
    return StringUtil.append(this.getConfig().getDomain(), String.valueOf(CharConstant.SLASH),
        res.jsonToObject(DefaultPutRet.class).key);
  }

  /**
   * @param res
   * @Return java.lang.String
   * @Author luffy
   * @Description 拼接七牛云文件访问url
   * @Date 2019-07-05 15:44
   */
  private String getUrl(@NotNull FetchRet res) throws Exception {
    if (this.getConfig().getDomain().endsWith(String.valueOf(CharConstant.SLASH))) {
      return StringUtil.append(this.getConfig().getDomain(), res.key);
    }
    return StringUtil.append(this.getConfig().getDomain(), String.valueOf(CharConstant.SLASH),
        res.key);
  }

  /**
   * @param file 文件(绝对路径)
   * @param fileName （文件名称 未null时会自动生成）
   * @Return java.lang.String
   * @author luffy
   * @Description 上传本地文件
   * @date 2019-07-05
   */
  private Response uploadLocal(@NotNull String file, @NotNull String fileName) throws Exception {
    return uploadManager.put(file, fileName, token);
  }

  /**
   * @param data 文件字节
   * @param fileName （文件名称 未null时会自动生成）
   * @Return com.qiniu.http.Response
   * @Author luffy
   * @Description 上传byte文件
   * @Date 2019-07-05 15:28
   */
  private Response uploadByte(@NotNull byte[] data, @NotNull String fileName) throws Exception {
    return uploadManager.put(data, fileName, token);
  }

  /**
   * @param inputStream 文件流
   * @param fileName （文件名称 未null时会自动生成）
   * @param mime 指定文件mimetype
   * @Return com.qiniu.http.Response
   * @Author luffy
   * @Description 上传流文件
   * @Date 2019-07-05 15:33
   */
  private Response uploadInputStream(@NotNull InputStream inputStream, @NotNull String fileName, @NotNull String mime)
      throws Exception {
    return uploadManager.put(inputStream, fileName, token, null, mime);
  }

  /**
   * @param remoteSrcUrl 网络文件地址
   * @param fileName 文件名称
   * @Return com.qiniu.storage.model.FetchRet
   * @Author luffy
   * @Description 上传网络文件
   * @Date 2019-07-05 15:41
   */
  private FetchRet uploadRemoteSrcUrl(@NotNull String remoteSrcUrl, String fileName) throws Exception {
    if (StringUtil.isEmpty(fileName)) {
      fileName = FileHelper.parseSuffix(remoteSrcUrl);
    }
    return bucketManager.fetch(remoteSrcUrl, super.getConfig().getBucketName(), fileName);
  }

  /**
   * @param file 文件(绝对路径)
   * @param fileName （文件名称 未null时会自动生成）
   * @Return java.lang.String
   * @Description 上传本地文件
   * @author luffy
   * @date 2019-07-05
   */
  @Override
  public String uploadForLocal(@NotNull String file, String fileName) throws Exception {
    if (StringUtil.isEmpty(fileName)) {
      fileName = FileHelper.getName(file);
    }
    return this.getUrl(this.uploadLocal(file, fileName));
  }

  /**
   * @param data 文件字节
   * @param fileName （文件名称 未null时会自动生成）
   * @param mime 指定文件mimetype
   * @Return java.lang.String
   * @Author luffy
   * @Description 上传byte文件
   * @Date 2019-07-05 15:28
   */
  @Override
  public String uploadForByte(@NotNull byte[] data, String fileName, @NotNull String mime) throws Exception {
    fileName = this.constructFileName(fileName, mime);
    return this.getUrl(this.uploadByte(data, fileName));
  }

  /**
   * @param inputStream 文件流
   * @param fileName （文件名称 未null时会自动生成）
   * @param mime 指定文件mimetype
   * @Return java.lang.String
   * @Author luffy
   * @Description 上传流文件
   * @Date 2019-07-05 15:33
   */
  @Override
  public String uploadForInputStream(@NotNull InputStream inputStream, String fileName, @NotNull String mime)
      throws Exception {
    fileName = this.constructFileName(fileName, mime);
    return this.getUrl(this.uploadInputStream(inputStream, fileName, mime));
  }

  /**
   * @param remoteSrcUrl 网络文件地址
   * @Return java.lang.String
   * @Author luffy
   * @Description 上传网络文件
   * @Date 2019-07-05 15:42
   */
  @Override
  public String uploadForRemoteSrcUrl(@NotNull String remoteSrcUrl, String fileName) throws Exception {
    if (StringUtil.isEmpty(fileName)) {
      fileName = FileHelper.parseSuffix(remoteSrcUrl);
    }
    return this.getUrl(this.uploadRemoteSrcUrl(remoteSrcUrl, fileName));
  }

  /**
   * @param fileName 文件名称
   * @param newMimeType 文件类型
   * @Return void
   * @Author luffy
   * @Description 修改文件类型
   * @Date 2019-07-05 15:45
   */
  @Override
  public void modifyMimeType(@NotNull String fileName, @NotNull String newMimeType) throws Exception {
    Response res = bucketManager.changeMime(super.getConfig().getBucketName(), fileName, newMimeType);
    if (!res.isOK()) {
      throw new FileUploadException(OssCode.FILE_UPDATE_FAILED, "七牛修改文件类型出错：" + res.error);
    }
  }

  /**
   * @param fileMimeMap 文件名称:文件类型
   * @Return java.util.HashMap<java.lang.String, java.util.Map < java.lang.String, java.lang.Object>>
   * @Author luffy
   * @Description 批量修改文件类型
   * @Date 2019-07-05 15:46
   */
  public HashMap<String, Map<String, Object>> batchModifyMimeType(@NotNull HashMap<String, String> fileMimeMap)
      throws Exception {
    BucketManager.BatchOperations batchOperations = new BucketManager.BatchOperations();
    //添加指令
    for (Map.Entry<String, String> entry : fileMimeMap.entrySet()) {
      String key = entry.getKey();
      String newMimeType = entry.getValue();
      batchOperations.addChgmOp(super.getConfig().getBucketName(), key, newMimeType);
    }
    BatchStatus[] batchStatusList = this.batchOpeate(batchOperations);
    HashMap<String, Map<String, Object>> retMap = new HashMap<>(fileMimeMap.size());
    int index = 0;
    for (Map.Entry<String, String> entry : fileMimeMap.entrySet()) {
      retMap.put(entry.getKey(), this.createMap(batchStatusList[index]));
      index += 1;
    }
    return retMap;
  }

  private BatchStatus[] batchOpeate(@NotNull BatchOperations batchOperations) throws Exception {
    Response response = bucketManager.batch(batchOperations);
    return response.jsonToObject(BatchStatus[].class);
  }

  private Map<String, Object> createMap(@NotNull BatchStatus batchStatus) {
    Map<String, Object> keyMap = new HashMap<>(2);
    keyMap.put("isOk", batchStatus.code == 200);
    keyMap.put("error", batchStatus.data.error);
    return keyMap;
  }

  /**
   * @param fileNames 文件名称
   * @Return java.util.HashMap<java.lang.String, java.util.Map < java.lang.String, java.lang.Object>>
   * @Author luffy
   * @Description 批量删除文件
   * @Date 2019-07-05 15:46
   */
  @Override
  public HashMap<String, Map<String, Object>> batchDelete(@NotNull String... fileNames) throws Exception {
    BucketManager.BatchOperations batchOperations = new BucketManager.BatchOperations();
    batchOperations.addDeleteOp(super.getConfig().getBucketName(), fileNames);
    BatchStatus[] batchStatusList = this.batchOpeate(batchOperations);
    HashMap<String, Map<String, Object>> retMap = new HashMap<>(fileNames.length);
    Map<String, Object> keyMap;
    int index = 0;
    for (String fileName : fileNames) {
      retMap.put(fileName, this.createMap(batchStatusList[index]));
      index += 1;
    }
    return retMap;
  }

  /**
   * @param fileName 文件名称
   * @param newFileName 新文件名称
   * @Return java.lang.String
   * @Author luffy
   * @Description 重命名
   * @Date 2019-07-05 15:47
   */
  @Override
  public String renameFile(@NotNull String fileName, @NotNull String newFileName) throws Exception {
    Response res = bucketManager.rename(super.getConfig().getBucketName(), fileName, newFileName);
    if (!res.isOK()) {
      throw new FileUploadException(OssCode.FILE_UPDATE_FAILED, "七牛重命名出错：" + res.error);
    }
    return this.getUrl(res);
  }

  /**
   * @param fileName 文件名称
   * @param newFileName 新文件名称
   * @Return java.lang.String
   * @Author luffy
   * @Description 强制复制文件
   * @Date 2019-07-05 15:49
   */
  public String copyFile(@NotNull String fileName, @NotNull String newFileName) throws Exception {
    Response res = bucketManager
        .copy(super.getConfig().getBucketName(), fileName, super.getConfig().getBucketName(), newFileName, false);
    if (!res.isOK()) {
      throw new FileUploadException(OssCode.FILE_UPDATE_FAILED, "七牛复制文件出错：" + res.error);
    }
    return this.getUrl(res);
  }

  /**
   * @param fileName 文件名称
   * @Return void
   * @Author luffy
   * @Description 删除文件
   * @Date 2019-07-05 15:49
   */
  @Override
  public void delete(@NotNull String fileName) throws Exception {
    Response res = bucketManager.delete(super.getConfig().getBucketName(), fileName);
    if (!res.isOK()) {
      throw new FileUploadException(OssCode.FILE_UPDATE_FAILED, "七牛删除文件出错：" + res.error);
    }
  }

  /**
   * @Author luffy
   * @Description 判断手否存在文件
   * @Param file
   * @Return boolean
   * @Date 2019-09-18 16:40
   */
  @Override
  public boolean isExists(@NotNull String file) throws Exception {
    FileInfo fileInfo = bucketManager.stat(super.getConfig().getBucketName(),file);
    if(null == fileInfo){
      return false;
    }
    return true;
  }}
